package com.qzct.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.qzct.config.MinioProperties;
import com.qzct.entity.MinioFile;
import com.qzct.entity.MyBucket;
import com.qzct.entity.vo.ResultCode;
import com.qzct.exceptions.ApiException;
import com.qzct.mapper.MinioFileMapper;
import com.qzct.service.MinioFileService;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;


/**
 *  服务层实现。
 *
 * @author 沈舜洁
 * @since 2024-12-25
 */
@Service
public class MinioFileServiceImpl extends ServiceImpl<MinioFileMapper, MinioFile> implements MinioFileService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private MinioClient minioClient;
    @Resource
    private MinioProperties minioProperties;
    @Resource
    private  MinioFileMapper minioFileMapper;

    /**
     * 格式化时间
     */
    private static final String DATE_FORMAT = "yyyyMMdd";

    /**
     * 字符集
     */
    private static final String ENCODING = "UTF-8";



    @Override
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build());
        } catch (Exception e) {
            logger.error("minio bucketExists Exception:{}", e);
        }
        return false;
    }
    /**
     * @Description: 创建 存储桶
     * @Param bucketName: 存储桶名称
     * @return: void
     * @Date: 2023/8/2 11:28
     */
    public void makeBucket(String bucketName) {
        try {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(getBucketName(bucketName)).build());
                logger.info("minio makeBucket success bucketName:{}", bucketName);
            }
        } catch (Exception e) {
            logger.error("minio makeBucket Exception:{}", e);
        }
    }

    @Override
    public MinioFile getOssInfo(String fileName) {
        try {
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder().bucket(getBucketName(minioProperties.getBucketName())).object(fileName)
                            .build());
            MinioFile ossFile = new MinioFile();
            ossFile.setName(ObjectUtil.isEmpty(stat.object()) ? fileName : stat.object());
            ossFile.setUrl(ossFile.getName());
            ossFile.setDomain(getOssHost(minioProperties.getBucketName()));
            ossFile.setHash(String.valueOf(stat.hashCode()));
            ossFile.setSize(stat.size());
            ossFile.setPutTime(new Date());
            ossFile.setContentType(stat.contentType());
            return ossFile;
        } catch (Exception e) {
            logger.error("minio getOssInfo Exception:{}", e);
        }
        return null;


    }

    @Override
    public MinioFile upLoadFile(String folderName, String fileName, MultipartFile file,String catename,Integer cateid,Integer albumid) {
        if (file == null || file.isEmpty()) {
            throw new ApiException(ResultCode.CUSTOM_ERROR,"文件不能为空");
        }
        // 文件大小
        if (file.getSize() > 20 * 1024 * 1024) {
            throw new ApiException(ResultCode.CUSTOM_ERROR,"文件大小不能大于20M");
        }
        String suffix = FileUtil.getSuffix(file.getOriginalFilename());
        // 文件后缀判断
        if (!CollUtil.contains(minioProperties.getFileExt(), suffix)) {
            String error = String.format("文件类型错误,目前支持[%s]等文件类型",
                    String.join(",", minioProperties.getFileExt()));
//            Map<String,Object> map=new HashMap<>();
//            map.put("error",error);
            throw new ApiException(ResultCode.CUSTOM_ERROR,error);
        }
        try {
            return upLoadFile(minioProperties.getBucketName(),folderName, fileName, suffix,file.getSize(),file.hashCode(), file.getInputStream(),file.getContentType(),catename,cateid,albumid);
        } catch (Exception e) {
            logger.error("minio upLoadFile Exception:{}", e);
            throw new ApiException(ResultCode.CUSTOM_ERROR,"文件上传失败");
        }
    }

    @Override
    public MinioFile upLoadImg(String folderName, String fileName, MultipartFile file,String catename,Integer cateid,Integer albumid) {

        if (file == null || file.isEmpty()) {
            throw new ApiException(ResultCode.CUSTOM_ERROR,"图片不能为空");
        }
        // 文件大小
        if (file.getSize() > 20 * 1024 * 1024) {
            throw new ApiException(ResultCode.CUSTOM_ERROR,"图片大小不能大于20M");
        }
        String suffix = FileUtil.getSuffix(file.getOriginalFilename());
        // 文件后缀判断
        if (!CollUtil.contains(minioProperties.getImgExt(), suffix)) {
            String error = String.format("图片类型错误,目前支持[%s]等文件类型",
                    String.join(",", minioProperties.getImgExt()));
//
            throw new ApiException(ResultCode.CUSTOM_ERROR,error);
        }
        try {
            return upLoadFile(minioProperties.getBucketName(),folderName, fileName, suffix,file.getSize(),file.hashCode(), file.getInputStream(),file.getContentType(),catename,cateid,albumid);
        } catch (Exception e) {
            logger.error("minio upLoadFile Exception:{}", e);
            throw new ApiException(ResultCode.CUSTOM_ERROR,e.getMessage());
        }
    }


    /**
     * @Description: 上传文件
     * @Param bucketName: 存储桶名称
     * @Param folderName: 上传的文件夹名称
     * @Param fileName: 上传文件名
     * @Param suffix: 文件后缀名
     * @Param stream: 文件流
     * @Param contentType: 文件类型
     * @Author: wmh
     * @Date: 2023/8/1 19:59
     */
    @SneakyThrows
    public MinioFile upLoadFile(String bucketName, String folderName, String fileName, String suffix,long size,int hash, InputStream stream,
                                String contentType,String catename,Integer cateid,Integer albumid) {
        if (!bucketExists(bucketName)) {
            logger.info("minio bucketName is not creat");
            makeBucket(bucketName);
        }
        MinioFile file = new MinioFile();
        String filePath = getFilePath(folderName, fileName, suffix);
        minioClient.putObject(PutObjectArgs.builder().bucket(getBucketName(bucketName)).object(filePath)
                .stream(stream, stream.available(), -1).contentType(contentType).build());
        file.setOriginalName(fileName);
        file.setName(fileName);
        file.setDomain(getOssHost(bucketName));
        file.setUrl(file.getDomain()+filePath);
        file.setPutTime(new Date());
        file.setContentType(contentType);
        file.setHash(String.valueOf(hash));
        file.setContentType("image/jpeg");
        file.setSize(size);
        file.setImageCateName(catename);
        file.setImageCateId(cateid);
        file.setAlbumId(albumid);
        System.out.println(file);
        minioFileMapper.insertSelective(file);
        stream.close();
        logger.info("minio upLoadFile success, filePath:{}", filePath);
        return file;
    }

    @Override
    public boolean removeFile(String fileName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder().bucket(getBucketName(minioProperties.getBucketName())).object(fileName)
                            .build());
            logger.info("minio removeFile success, fileName:{}", fileName);
            return true;
        } catch (Exception e) {
            logger.error("minio removeFile fail, fileName:{}, Exception:{}", fileName, e);
        }
        return false;

    }

    @Override
    public boolean removeFiles(List<String> fileNames) {
        try {
            Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new);
            minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(getBucketName(minioProperties.getBucketName()))
                    .objects(stream::iterator).build());
            logger.info("minio removeFiles success, fileNames:{}", fileNames);
            return true;
        } catch (Exception e) {
            logger.error("minio removeFiles fail, fileNames:{}, Exception:{}", fileNames, e);
        }
        return false;


    }

    @Override
    public void downloadFile(HttpServletResponse response, String fileName, String filePath) {
        GetObjectResponse is = null;
        try {
            GetObjectArgs getObjectArgs =
                    GetObjectArgs.builder().bucket(minioProperties.getBucketName()).object(filePath)
                            .build();
            is = minioClient.getObject(getObjectArgs);
            // 设置文件ContentType类型，这样设置，会自动判断下载文件类型
            response.setContentType("application/x-msdownload");
            response.setCharacterEncoding(ENCODING);
            // 设置文件头：最后一个参数是设置下载的文件名并编码为UTF-8
            response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, ENCODING));
            IoUtil.copy(is, response.getOutputStream());
            logger.info("minio downloadFile success, filePath:{}", filePath);
        } catch (Exception e) {
            logger.error("minio downloadFile Exception:{}", e);
        } finally {
            IoUtil.close(is);
        }


    }

    @Override
    public List<MyBucket> findAllBuckets() throws Exception {
       // List<Bucket> bucketList=null;

        List<Bucket> buckets= minioClient.listBuckets();
        List<MyBucket> myBuckets= new ArrayList<>();

        if(CollUtil.isEmpty(buckets)){
            throw new ApiException(ResultCode.CUSTOM_ERROR,"No Buckets");

        }
         MyBucket myBucket=null;
        for (Bucket item:buckets) {
            myBucket = new MyBucket();
            myBucket.setName(item.name());
            myBucket.setZonedDateTime(item.creationDate());

            myBuckets.add(myBucket);
        }

        return  myBuckets;


    }

    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param fileName   文件名称
     * @param expires    过期时间
     * @return url
     */
    public String getPresignedObjectUrl(String bucketName, String fileName, Integer expires) {
        String link = "";
        try {
            link = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(getBucketName(bucketName))
                            .object(fileName).expiry(expires).build());
        } catch (Exception e) {
            logger.error("minio getPresignedObjectUrl is fail, fileName:{}", fileName);
        }
        return link;
    }

    /**
     * 根据规则生成存储桶名称规则
     *
     * @param bucketName 存储桶名称
     * @return String
     */
    private String getBucketName(String bucketName) {
        return bucketName;
    }

    /**
     * 根据规则生成文件路径
     *
     * @param folderName       上传的文件夹名称
     * @param originalFilename 原始文件名
     * @param suffix           文件后缀名
     * @return string 上传的文件夹名称/yyyyMMdd/原始文件名_时间戳.文件后缀名
     */
    private String getFilePath(String folderName, String originalFilename, String suffix) {
        return StrPool.SLASH + String.join(StrPool.SLASH, folderName, DateUtil.date().toString(DATE_FORMAT),
                originalFilename) + StrPool.C_UNDERLINE + DateUtil.current() + StrPool.DOT + suffix;
    }

    /**
     * 获取域名
     *
     * @param bucketName 存储桶名称
     * @return String
     */
    public String getOssHost(String bucketName) {
        return minioProperties.getEndpoint()+ StrPool.SLASH + getBucketName(bucketName);
    }


}
